/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package dischargesimulation.param;


import dischargesimulation.lib.Numbers;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Properties;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

/**
 *
 * @author rybakov
 */
public class Params implements Serializable{
    
            
    final int MAX_PARAM=200;
    enum Locale{RUSSIAN,ENGLISH}
    final Locale currentLocate = Locale.ENGLISH;
    
    /**
     * supported parameter types 
     */
    enum ParamType { INT/*intParams*/, FLOAT/*floatParams*/, CHOISELIST /*intParams*/, STRING /*strParams*/, BOOLEAN/*boolParams*/ }
    
    String    strParams[]   = new String[MAX_PARAM];
    
    /** storage for float values 
     * 
     */
    double    floatParams[] = new double[MAX_PARAM];
    
    
    
    /** storage for int values and ChoiseLists
     * 
     */
    int       intParams[]   = new int[MAX_PARAM];
    
    
    /** storage for boolean and checkboxes
     * 
     */
    boolean   boolParams[]   = new boolean[MAX_PARAM];
//    ParamType paramsTypes[] = new ParamType[MAX_PARAM];
    ParamInfo paramInfo[] = new ParamInfo[MAX_PARAM];
    
//    JComponent [] paramComponents = new JComponent[MAX_PARAM];
            
    class ChoiseListItem{
        short value;
        String label_en;
        String label_ru;
        boolean isDefault;
        // <editor-fold defaultstate="collapsed" desc="methods">
        public ChoiseListItem(short value, String label_en, String label_ru) {
            this.value = value;
            this.label_en = label_en;
            this.label_ru = label_ru;
            this.isDefault=false;
        }
        public ChoiseListItem(short value, String label_en, String label_ru, boolean isDefault) {
            this.value = value;
            this.label_en = label_en;
            this.label_ru = label_ru;
            this.isDefault = isDefault;
        }
        public String getLabel(){
            if(currentLocate==Locale.RUSSIAN){
                return label_ru;
            }else{
                return label_en;
            }
        }

        @Override
        public String toString() {
            if(currentLocate == Locale.RUSSIAN){
                return label_ru;
            }else{
                return label_en;
            }            
        }
                
        // </editor-fold>
    }
    
    // one parameter full information for editing
    class ParamInfo{
        ParamType type;
        int paramIndex;
        String label_en;
        String label_ru;
        String toolTip_en;
        String toolTip_ru;
        int choiseListCount=0;
        ChoiseListItem [] choiseList;
        String categories;
        double defaultFloatValue;   
        int defaultIntValue;
        int labelFontStyle=0;
        
        JLabel label;
        JComponent uiComponent;      
        ValueUpdateListener valueUpdateListener;
        ActionListener customUpdateAction;
        
        // <editor-fold defaultstate="collapsed" desc="methods">
        public ParamInfo(int paramIndex, ParamType type, double defaultFloatValue) {
            this.paramIndex = paramIndex;
            this.type = type;
            paramInfo[paramIndex] = this;            
            this.defaultFloatValue=defaultFloatValue;
            if(type==ParamType.FLOAT){
                FloatEdit fe;
                fe=new FloatEdit(this);
                this.uiComponent=fe;
                this.valueUpdateListener=fe;                
                floatParams[this.paramIndex] =  defaultFloatValue;
            }
             
            if(type==ParamType.BOOLEAN){
                BooleanEdit be;
                be=new BooleanEdit(this);
                this.uiComponent=be;
                this.valueUpdateListener=be;              
                boolParams[this.paramIndex] = defaultFloatValue>0;
            }
            if(type==ParamType.CHOISELIST){
                ChoiseEdit ce;
                ce = new ChoiseEdit(this);
                this.uiComponent=ce; 
                this.valueUpdateListener=ce;                   
                intParams[this.paramIndex] = defaultIntValue;
            }
            
            
        }
        public String getLabel(){
            if(currentLocate == Locale.RUSSIAN) {
                return label_ru;
            }
            else{
                return label_en;
            }
        }   
        public String getToolTip(){
            if(currentLocate == Locale.RUSSIAN) {
                return toolTip_ru;
            }
            else{
                return toolTip_en;
            }
        }
        public void addChoise(short value, String label_en, String label_ru, boolean isDefault){
            if(choiseList==null){
                choiseList = new ChoiseListItem[20];
            }
            if(isDefault){
                defaultIntValue=value;
            }
            ChoiseListItem item = new ChoiseListItem(value, label_en, label_ru,isDefault);
            choiseList[choiseListCount] = item;  
            choiseListCount++;
            
            ChoiseEdit ce = (ChoiseEdit)this.uiComponent;
            ce.removeItemListener(ce);
            ce.addItem(item);
            ce.addItemListener(ce);
        }
        public void addChoise(short value, String label_en, String label_ru ){
            addChoise(  value,   label_en,   label_ru, false);
        }
        // </editor-fold>         

      

        
    }
    
    int getInt(int i){
        return intParams[i];
    }
    void setInt(int i, int value){
        intParams[i]=value;
    }
    boolean getBoolean(int i){
        return boolParams[i];
    }
    void setBoolean(int i, boolean value){
        boolParams[i]=value;
    }
    double get(int i){
        return floatParams[i];
    }
    void set(int i, double value){
        floatParams[i]=value;
    }
    
    /** every edit component supports this interface to display changes in corresponding array cells
     * 
     */
    interface ValueUpdateListener {
      public void checkValueUpdated();
    }
    /** Component for editing float value
     * 
     */
    class FloatEdit extends JTextField implements DocumentListener,ValueUpdateListener{
         
        ParamInfo pi;
         // <editor-fold defaultstate="collapsed" desc="methods">
        
             
        public void checkValueUpdated(){
            
            double f1 = floatParams[pi.paramIndex] ;
            if(!Numbers.isCorrectFloat(this.getText()))return ;
            double f2 = Numbers.strToNumber(this.getText());
            if(Math.abs(f1-f2)>1e-5){
                this.getDocument().removeDocumentListener(this);
                this.setText(Numbers.numberToStr(f1));
                this.getDocument().addDocumentListener(this);
            }
            
        }
        
        void textUpdated(){
            if(Numbers.isCorrectFloat(this.getText())){
                this.setBackground(Color.white);
                floatParams[pi.paramIndex] = Numbers.strToNumber(this.getText());
                checkValuesUpdated();
            }else{
                this.setBackground(Color.red);
            }
            
        }
        
        
        public FloatEdit(ParamInfo pi) {
            this.pi = pi;
            
            this.setToolTipText(pi.getToolTip());
            
            this.setText(Numbers.numberToStr(pi.defaultFloatValue));
            this.getDocument().addDocumentListener(this);// события ищменения текста
            
        }

        // события изменения текста
        public void insertUpdate(DocumentEvent e) {
            textUpdated();
        }

        public void removeUpdate(DocumentEvent e) {
           textUpdated();
        }

        public void changedUpdate(DocumentEvent e) {
           textUpdated();
        }
        
        // </editor-fold>    

        

        
    }
    class BooleanEdit extends JCheckBox implements ItemListener, ValueUpdateListener{
         ParamInfo pi;
        // <editor-fold defaultstate="collapsed" desc="methods">
  
        public BooleanEdit(ParamInfo pi) {
            this.pi = pi;
            this.addItemListener(this);
        }

        public void itemStateChanged(ItemEvent e) {
            boolParams[pi.paramIndex] = this.isSelected();
            checkValuesUpdated();
        }
        
        public void checkValueUpdated() {
            if(boolParams[pi.paramIndex] != this.isSelected()){
                 this.removeItemListener(this);
                 this.setSelected(boolParams[pi.paramIndex]);                  
                 this.addItemListener(this);
            }
        }
        // </editor-fold>    
    }
    
    class ChoiseEdit extends JComboBox implements ItemListener, ValueUpdateListener{
        ParamInfo pi;
        
        public ChoiseEdit(ParamInfo pi) {
            this.pi = pi;
            
            for(int i=0;i<pi.choiseListCount;i++){
                this.addItem(pi.choiseList[i] );
                if(pi.choiseList[i].isDefault){
                    this.setSelectedIndex(i);
                }
            }
//             this.addActionListener(this);
             this.addItemListener(this);
        }
        
        public void checkValueUpdated() {
            Object o = this.getSelectedItem();
            if(o==null)return ;
 
            try {
                ChoiseListItem item = ((ChoiseListItem) o);
                if (item == null) {
                    return;
                }
                
                if (intParams[pi.paramIndex] != item.value) {
                    for (int i = 0; i < pi.choiseListCount; i++) {
                         
                        
                        ChoiseListItem item1 = ((ChoiseListItem) this.getItemAt(i));
                        if (intParams[pi.paramIndex] == item1.value) {
                            this.removeItemListener(this);
                            this.setSelectedIndex(i);
                            this.addItemListener(this);
                            return;
                        }
                    }
                }
            } catch (Exception e) {
                System.out.println("checkValueUpdated "+e.toString());
            }
            
        }// class ChoiseEdit

        public void itemStateChanged(ItemEvent e) {
            try{
                Object o = this.getSelectedItem();
                if(o==null || !(o instanceof ChoiseListItem)) return ;
                ChoiseListItem item = ((ChoiseListItem)o );

//                System.out.println(pi.paramIndex);
//                System.out.println(item.value);
//                System.out.println();
                intParams[pi.paramIndex]=item.value;
                checkValuesUpdated();
            }catch(Exception ex){
                System.out.println("itemStateChanged "+ex.toString());
            }
        }
        
    }
    
    public void checkValuesUpdated(){
        for(int i=0; i<paramInfo.length; i++){
            ParamInfo pi = paramInfo[i];
            if(pi!=null ){
                
              if( pi.valueUpdateListener!=null){
                      pi.valueUpdateListener.checkValueUpdated();
               }
               if(pi.customUpdateAction!=null){
                   ActionEvent ae = new ActionEvent(pi, i, null);
                   pi.customUpdateAction.actionPerformed(ae);
               }
            }
        }
        
    }
    
    final static short PARAM_TEST_FLOAT1 = 1;
    final static short PARAM_TEST_FLOAT2 = 2;
    final static short PARAM_TEST_BOOLEAN3 = 3;
    final static short PARAM_TEST_CHOISELIST4 = 4;
   
    final static short PARAM_VOLT_AMLITUDE = 5;
    final static short PARAM_VOLT_FREQUENCY = 6;
 
    final static short PARAM_NEGATIVE_DETACH_EP = 7;
    final static short PARAM_ELECTRON_DIFFUSION_COEF = 8;
    final static short PARAM_ELECTRON_MOBILITY = 9;
    final static short PARAM_ELECTRON_EMISSION = 10;
    final static short PARAM_ELECTRON_SECONDARY_BURN_PROBABILITY = 11;
    
    final static short PARAM_ELECTRODE_RADIUS = 12;
    final static short PARAM_DIELECTRIC_HEIGHT = 13;
     
    
    
    
    
 
    public void initParams__000(){
         // <editor-fold defaultstate="collapsed" desc="long init">
       
        
        ParamInfo pi = new ParamInfo(PARAM_TEST_FLOAT1, ParamType.FLOAT, 10e10);
        pi.categories = "TEST";       
        pi.label_en = "test   ";
        pi.label_ru  = "тест";
        pi.toolTip_en = "test tooltip";
        pi.toolTip_ru = "тест туултип";
        pi.labelFontStyle = Font.BOLD;
        
        
        pi = new ParamInfo(PARAM_TEST_FLOAT2,ParamType.FLOAT,20e10);
        pi.categories = "TEST2";        
        pi.label_en = "test2";
        pi.label_ru  = "тест2";
        pi.toolTip_en = "test tooltip2";
        pi.toolTip_ru = "тест туултип2";
    
        
        pi = new ParamInfo(PARAM_TEST_BOOLEAN3,ParamType.BOOLEAN,1);
        pi.categories = "TEST3";
        pi.label_en = "test3";
        pi.label_ru  = "тест3";
        pi.toolTip_en = "test tooltip3";
        pi.toolTip_ru = "тест туултип3";
        
        
        pi = new ParamInfo(PARAM_TEST_CHOISELIST4,ParamType.CHOISELIST,0);
        pi.categories = "TEST4";        
        pi.label_en = "test4";
        pi.label_ru  = "тест4";
        pi.toolTip_en = "test tooltip4";
        pi.toolTip_ru = "тест туултип4";
        pi.addChoise((short)1, "test41", "тест41");
        pi.addChoise((short)2, "test42", "тест42");
        pi.addChoise((short)3, "test43", "тест43",true);
        
        pi.customUpdateAction = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
              ParamInfo this_pi = (ParamInfo)e.getSource();
              this_pi.uiComponent.setEnabled(getBoolean( PARAM_TEST_BOOLEAN3 ));
            }
        };
        
        
        pi = new ParamInfo(PARAM_VOLT_AMLITUDE,ParamType.FLOAT,5e3);
        pi.categories = "ELECTRODE GEN";
        pi.label_en = "Voltage amlitude (Volt)";
        pi.label_ru  = "Амплитуда напряжения (Вольт)";
        pi.toolTip_en = "";
        pi.toolTip_ru = "";
        
        pi = new ParamInfo(PARAM_VOLT_FREQUENCY,ParamType.FLOAT,3e3);
        pi.categories = "ELECTRODE GEN";
        pi.label_en = "Voltage frequency (1/second)";
        pi.label_ru  = "Частота напряжения (1/секунду)";
        pi.toolTip_en = "";
        pi.toolTip_ru = "";
        
        
        
        // </editor-fold> 
    }
    
    javax.swing.JDialog dialog;
    boolean dialogOKPressed=false;
    boolean dialogShown = false;
    
    void initDialog(){
        if(dialog != null){
            dialog.dispose();
        }
        dialog=new javax.swing.JDialog((JFrame)null,true);
        dialog.setSize(300, 400);
//        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        
        GridLayout gridLayout = new GridLayout(0,2);
        dialog.setLayout(gridLayout);
        
        
       
    }
    void centerDialog(int h){
        dialog.setSize(400, h);
        final Toolkit toolkit = Toolkit.getDefaultToolkit();
        final Dimension screenSize = toolkit.getScreenSize();
        final int x = (screenSize.width - dialog.getWidth()) / 2;
        final int y = (screenSize.height - dialog.getHeight()) / 2;
        dialog.setLocation(x, y);
    }
    
    public boolean showParams(String category, String ok_label){
        int rows = 0;
        initDialog();
//        Component [] cs =   frame.getComponents().clone();
//        for(int i=0;i<cs.length;i++){
//            frame.remove(cs[i]);
//        }
//        frame.validate();
        for(int i=0;i<MAX_PARAM;i++ ){
            
            if(paramInfo[i]!=null
            && paramInfo[i].categories.contains(category)
            ){
                JLabel lbl = new JLabel(paramInfo[i].getLabel());
                lbl.setToolTipText(paramInfo[i].getToolTip());
                 
                 if(paramInfo[i].labelFontStyle != 0  ){
                    Font fnt = lbl.getFont();
                    Font newLabelFont=new Font(
                            fnt.getName(),
                            paramInfo[i].labelFontStyle,
                            (int)Math.round(fnt.getSize()*1.3)
                             );  
                    lbl.setFont(newLabelFont);
                    rows++;
                }
                 
                class LabelMouseListener implements MouseListener{
                       // <editor-fold defaultstate="collapsed" desc="class content">
       
                    int i;

                    public LabelMouseListener(int i) {
                        this.i = i;
                    }
                    public void mouseClicked(MouseEvent e) {
                        JComponent c = paramInfo[i].uiComponent;
                        
                        if(!c.isEnabled() || !c.isVisible()) return ;
                        
                        if(paramInfo[i].uiComponent instanceof BooleanEdit){
                            BooleanEdit be = (BooleanEdit)paramInfo[i].uiComponent;
                            be.setSelected(! be.isSelected());
                            return ;
                        }
                        
                        if(paramInfo[i].uiComponent instanceof ChoiseEdit){
                            ChoiseEdit ce  = (ChoiseEdit)paramInfo[i].uiComponent; 
                            ce.showPopup();
                         
                        }else{
                            
                            paramInfo[i].uiComponent.requestFocus(); 
                        }
                    }

                    public void mousePressed(MouseEvent e) {
                       
                    }

                    public void mouseReleased(MouseEvent e) {
                       
                    }

                    public void mouseEntered(MouseEvent e) {
                       
                    }

                    public void mouseExited(MouseEvent e) {
                       
                    }
                    // </editor-fold>
                }
                lbl.addMouseListener(new LabelMouseListener(i));
                
                dialog.add(lbl);
                dialog.add(paramInfo[i].uiComponent);
            }
        }
        dialog.validate();
        
        dialogOKPressed=false;
        
        // нажатие кнопок по закрытию формы
        class ButtonPressedAction implements ActionListener{
            // <editor-fold defaultstate="collapsed" desc="methods">
  
            boolean result;
            public void actionPerformed(ActionEvent e) {
                dialogOKPressed = result;
                dialog.setVisible(false);
                dialog.dispose();
            }

            public ButtonPressedAction(boolean result) {
                this.result = result;
            }
            // </editor-fold>              
            
        }
        JButton b_cancel = new JButton("Cancel");
        b_cancel.addActionListener(new ButtonPressedAction(false));
        JButton b_ok = new JButton(ok_label);
        b_ok.addActionListener(new ButtonPressedAction(true));
        
        dialog.add(b_cancel);
        dialog.add(b_ok);
        rows++;
        
        centerDialog(rows*100);
        
        // Thead for value changed monitoring and displaying
        Thread updateThread = new Thread(){
            // <editor-fold defaultstate="collapsed" desc="methods">
            public void run() {
               while(!isInterrupted()) {
                   checkValuesUpdated();                  
                   try {

                        Thread.sleep(3000);
                        
                    } catch (InterruptedException ex) {
//                        Logger.getLogger(Params.class.getName()).log(Level.SEVERE, null, ex);
                        return ;
                    }
               }
            }
            // </editor-fold>
        };
        updateThread.start();
        dialog.setVisible(true);
        updateThread.interrupt();
        
        return  dialogOKPressed;
    }
    
    public static void main(String[]args)throws Exception{
//        System.out.print(Locale.values());
        final Params p = new Params();
        
        p.initParams__000();
        
        Thread t = new Thread() {
// <editor-fold defaultstate="collapsed" desc="methods">
   
            public void run() {
                while(!isInterrupted()){
                p.floatParams[1] += 100;
                p.boolParams[PARAM_TEST_BOOLEAN3]=!p.boolParams[PARAM_TEST_BOOLEAN3];
                
                int a =   p.intParams[PARAM_TEST_CHOISELIST4] ;
                a=a+1;
                if(a>2){a=1;}
                p.intParams[PARAM_TEST_CHOISELIST4]=a;
                // <editor-fold defaultstate="collapsed" desc="sleep">
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
//                    Logger.getLogger(Params.class.getName()).log(Level.SEVERE, null, ex);
                    return ;
                }
                 // </editor-fold>  
                }
            }
             // </editor-fold>  
        };
        //t.start();
        
        try {
            p.loadFromFile("test1.properties", null);
        } catch (IOException iOException) {
        }
        if(     p.showParams("TEST","Save") ){
                p.saveToFile("test1.properties", null);
        }
        t.interrupt();
//        p.dialog.dispose();
    }

    
    
    public Params() {
        initParams__000();
    }
    public void saveToFile(String filename, String category) throws FileNotFoundException, IOException{
        Properties p = new Properties();
      
        for(int i=0;i<MAX_PARAM;i++){
            ParamInfo pi = paramInfo[i];
            if(pi!=null
            && (category==null || pi.categories.contains(category))
            ){
                String s="";
                if(pi.type==ParamType.BOOLEAN){
                    s=Boolean.toString(boolParams[pi.paramIndex]);
                }
                if(pi.type==ParamType.FLOAT){
                    s=Double.toString(floatParams[pi.paramIndex]);
                }
                if(pi.type==ParamType.INT || pi.type==ParamType.CHOISELIST ){
                    s=Integer.toString(intParams[pi.paramIndex]);
                }
                if(pi.type==ParamType.STRING ){
                    s=strParams[pi.paramIndex];
                }

                Object tmp=p.setProperty( pi.type.toString() +"."+Integer.toString(i), s);
                
            }
        }
        File f = new File(filename);
        FileOutputStream fos = new FileOutputStream(f);
        p.store(fos, filename);
        fos.close();
    }

    public void loadFromFile(String filename, String category) throws FileNotFoundException, IOException{
        Properties p = new Properties();
        File f = new File(filename);
        FileInputStream fis = new FileInputStream(f);
        p.load(fis);
        fis.close();
        Enumeration e = p.propertyNames();
        while (e.hasMoreElements()) {
            String key = (String) e.nextElement();
            String value = p.getProperty(key);
            if(key.contains(".")){
                int dot_pos = key.indexOf(".");
                String s_type = key.substring(0, dot_pos);
                String s_idx = key.substring( dot_pos+1);
                
                ParamType type  = ParamType.valueOf(s_type);
                int index = Integer.parseInt(s_idx);
                
                
                if(type==ParamType.BOOLEAN){
                    boolParams[index] =  value.equals("true");
                }
               
                if(type==ParamType.FLOAT){
                    floatParams[index]  =Double.parseDouble(value);
                }
                if(type==ParamType.INT || type==ParamType.CHOISELIST ){
                    intParams[index]=Integer.parseInt(value);
                }
                if(type==ParamType.STRING ){
                    strParams[index]=value;
                }
 
                
            }
        }
        
    }

}
